using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class WeaponItem : InventoryItem{
	public int dieCount = 1;
	public int dieType = 8;
	
	public List<string> AttackAction = new List<string>{"BAB","BAB-5","BAB-10","BAB-15"};
	
	public bool TwoHanded = false;
	public bool LightWeapon = false;
	public bool itemDescription = false;
	public bool currentlyDrawn = false;
	public bool InOffHand = false;
	
	public bool DontShowInInventory = false;
	
	public bool Ranged = false;
	public int RangeIncrement = 10;
	public bool RequiresAmmunition = true;
	
	public string DamageType = "B";
	
	public bool AdvancedAttackOverride = false;
	public bool AdvancedDamageOverride = false;
	
	public string _AttackBonusFunction = "BAB";
	public string _DamageBonusFunction = "Strength";
	
	public int critBounds = 20;
	public int critMultiplier = 2;
	
	private string _weaponDescription;
	public string weaponDescription
	{
		get
		{
			if(string.IsNullOrEmpty(_weaponDescription))
			{
				SheetManager.activeCharacter.SetMathParserStats();
				
				string retStr = "";
				
				foreach(string s in AttackAction)
				{
					int val = Mathf.FloorToInt(MathParser.Evaluate(s));
					if(val > 0)
					{
						val += Mathf.FloorToInt(MathParser.Evaluate(AttackBonusFunction));
						retStr += ((val >= 0)?"+":"")+val+"/";
					}
				}
				
				retStr = retStr.TrimEnd('/');
				
				retStr += ", " + DescriptionDamageAverage + " Avg. Dam.";
				
				retStr += ", " + critBounds + ((critBounds < 20)?"-20":"") + " x" + critMultiplier;
				
				buffs.ForEach((b) =>
				{
					b.modifiers.FindAll(m => m.toStat == "Crit. Damage").ForEach( m =>
					{
						retStr += " +" + ((m.dicep > 0)?(m.dicep+"d"):"") + m.amount;
					});
				});
				_weaponDescription = retStr;
			}
			return _weaponDescription;
		}
	}
	
	public string AttackBonusFunction
	{
		get
		{
			if(AdvancedAttackOverride)
				return _AttackBonusFunction;
			
			string func = "";
			if(Ranged)
				func += "Dexterity";
			else
				func += "Strength";
			return func;
		}
	}
	
	public string DamageBonusFunction
	{
		get
		{
			if(AdvancedDamageOverride)
				return _DamageBonusFunction;
			
			string func = "";
			if(Ranged)
				func += "0";
			else
			{
				func += "Strength";
				if(TwoHanded)
					func += "*1.5";
				if(InOffHand)
					func += "/2";
			}
			return func;
		}
	}
	
	List<RollDefinition> _MagicAttackBonus;
	public List<RollDefinition> MagicAttackBonus
	{
		get
		{
			if(_MagicAttackBonus == null)
			{
				List<RollDefinition> rolls = new List<RollDefinition>();
				Dictionary<string,int> reference = new Dictionary<string, int>();
				buffs.ForEach((b) =>
					{
						b.modifiers.FindAll(m => m.toStat == "Attacks" && m.dicep == 0).ForEach( m =>
							{
								if(reference.ContainsKey(m.type))
								{
									if(reference[m.type] < m.amount)
										reference[m.type] = m.amount;
								}
								else
									reference.Add(m.type, m.amount);
							}
						);
					
						b.modifiers.FindAll(m => m.toStat == "Attacks" && m.dicep > 0).ForEach( m =>
							{
								RollDefinition rd = new RollDefinition();
								rd.dieCount = m.dicep;
								rd.dieType = m.amount;
								rd.type = m.type;
								rolls.Add(rd);
							}
						);
					}	
				);
				
				foreach(string t in reference.Keys)
				{
					RollDefinition rd = new RollDefinition();
					
					rd.dieCount = 0;
					rd.dieType = 0;
					rd.bonusAmount = reference[t];
					rd.type = t;
					
					rolls.Add(rd);
				}
				
				_MagicAttackBonus = rolls;
			}
			
			return _MagicAttackBonus;
		}
	}
	
	List<RollDefinition> _MagicDamageBonus;
	public List<RollDefinition> MagicDamageBonus
	{
		get
		{
			if(_MagicDamageBonus == null)
			{
				List<RollDefinition> rolls = new List<RollDefinition>();
				Dictionary<string,int> reference = new Dictionary<string, int>();
				buffs.ForEach((b) =>
					{
						b.modifiers.FindAll(m => m.toStat == "Damage" && m.dicep == 0).ForEach( m =>
							{
								if(reference.ContainsKey(m.type))
								{
									if(reference[m.type] < m.amount)
										reference[m.type] = m.amount;
								}
								else
									reference.Add(m.type, m.amount);
							}
						);
					}	
				);
				
				foreach(string t in reference.Keys)
				{
					RollDefinition rd = new RollDefinition();
					
					rd.dieCount = 0;
					rd.dieType = 0;
					rd.bonusAmount = reference[t];
					rd.type = t;
					
					rolls.Add(rd);
				}
				
				_MagicDamageBonus = rolls;
			}
			
			return _MagicDamageBonus;
		}
	}
	
	List<RollDefinition> _MagicDamageDice;
	public List<RollDefinition> MagicDamageDice
	{
		get
		{
			if(_MagicDamageDice == null)
			{
				List<RollDefinition> rolls = new List<RollDefinition>();
				buffs.ForEach((b) =>
					{				
						b.modifiers.FindAll(m => m.toStat == "Damage" && m.dicep > 0).ForEach( m =>
							{
								RollDefinition rd = new RollDefinition();
								rd.dieCount = m.dicep;
								rd.dieType = m.amount;
								rd.type = m.type;
								rolls.Add(rd);
							}
						);
					}	
				);
				
				_MagicDamageDice = rolls;
			}
			
			return _MagicDamageDice;
				
		}
	}
	
	List<RollDefinition> _CritDamage;
	public List<RollDefinition> CritDamage
	{
		get
		{
			if(_CritDamage == null)
			{
				List<RollDefinition> rolls = new List<RollDefinition>();
				Dictionary<string,int> reference = new Dictionary<string, int>();
				buffs.ForEach((b) =>
					{				
						b.modifiers.FindAll(m => m.toStat == "Crit. Damage" && m.dicep > 0).ForEach( m =>
							{
								RollDefinition rd = new RollDefinition();
								rd.dieCount = m.dicep;
								rd.dieType = m.amount;
								rd.type = m.type;
								rolls.Add(rd);
							}
						);
					
						b.modifiers.FindAll(m => m.toStat == "Crit. Damage" && m.dicep == 0).ForEach( m =>
							{
								if(reference.ContainsKey(m.type))
								{
									if(reference[m.type] < m.amount)
										reference[m.type] = m.amount;
								}
								else
									reference.Add(m.type, m.amount);
							}
						);
					}	
				);
				
				foreach(string t in reference.Keys)
				{
					RollDefinition rd = new RollDefinition();
					
					rd.dieCount = 0;
					rd.dieType = 0;
					rd.bonusAmount = reference[t];
					rd.type = t;
					
					rolls.Add(rd);
				}
				
				_CritDamage = rolls;
			}
			
			return _CritDamage;
				
		}
	}
	
	//For Description Only
	public int DescriptionDamageAverage
	{
		get
		{
			Dictionary<string,int> reference = new Dictionary<string, int>();
			buffs.ForEach((b) =>
				{
					b.modifiers.FindAll(m => m.toStat == "Damage" && m.dicep == 0).ForEach( m =>
						{
							if(reference.ContainsKey(m.type))
							{
								if(reference[m.type] < m.amount)
									reference[m.type] = m.amount;
							}
							else
								reference.Add(m.type, m.amount);
						}
					);
					b.modifiers.FindAll(m => m.toStat == "Damage" && m.dicep > 0).ForEach( m =>
						{
							if(reference.ContainsKey(m.type))
							{
								reference[m.type] += Mathf.FloorToInt(m.dicep * m.amount/2f);
							}
							else
								reference.Add(m.type, Mathf.FloorToInt(m.dicep * m.amount/2f));
						}
					);
				}	
			);
			int amount = 0;
			foreach(int a in reference.Values)
			{
				amount += a;
			}
			
			amount += (int)MathParser.Evaluate(DamageBonusFunction);
			amount += (int)(dieType/2f * dieCount);
			
			return amount;
		}
	}
	
	public void RollDamage(bool critical = false, bool sneakAttack = false)
	{		
		SheetManager.activeCharacter.SetMathParserStats();
		
		List<RollDefinition> rolls = new List<RollDefinition>();

		rolls.Add(new RollDefinition(dieCount, dieType, "", (int)MathParser.Evaluate(DamageBonusFunction)));
		rolls.AddRange(MagicDamageBonus);
		
		Dictionary<string, List<TemporaryBonus>> damageBuffs;
		
		if(critical)
		{
			for(int i = 1; i < critMultiplier; i++)
			{
				rolls.Add(new RollDefinition(dieCount, dieType, "", (int)MathParser.Evaluate(DamageBonusFunction)));
				rolls.AddRange(MagicDamageBonus);
			}
			
			damageBuffs = SheetManager.activeCharacter.GetBuffMods("Crit. Damage");
			foreach(string type in damageBuffs.Keys)
			{
				foreach(TemporaryBonus b in damageBuffs[type])
				{
					rolls.Add(new RollDefinition(b.dicep,b.amount,type,((b.dicep == 0)?b.amount:0)));
				}
			}
			
			rolls.AddRange(CritDamage);
		}
		
		rolls.AddRange(MagicDamageDice);
		
		damageBuffs = SheetManager.activeCharacter.GetBuffMods("Damage");
		foreach(string type in damageBuffs.Keys)
		{
			foreach(TemporaryBonus b in damageBuffs[type])
			{
				rolls.Add(new RollDefinition(b.dicep,b.amount,type,((b.dicep == 0)?b.amount:0)));
			}
		}
		
		if(sneakAttack && SheetManager.activeCharacter.sneakDice > 0)
		{
			rolls.Add(new RollDefinition(SheetManager.activeCharacter.sneakDice, 6, "Sneak"));
		}
		
		GameReference.reference.StartCoroutine(HelperFunctions.RollWindow(rolls, "Damage"));
	}
	
	public void RollAttack(bool fullAttack = false, bool flurryOfBlows = false)
	{
		SheetManager.activeCharacter.SetMathParserStats();
		
		List<RollDefinition> rolls = new List<RollDefinition>();
		rolls.AddRange(MagicAttackBonus);
		rolls.Add(new RollDefinition());
		
		RollDefinition AB = new RollDefinition();
		AB.dieCount = 0;
		AB.bonusAmount = (int)MathParser.Evaluate(AttackAction[0] + "+" + AttackBonusFunction); 
		
		if(flurryOfBlows)
			AB.bonusAmount += (int)MathParser.Evaluate("<floor>Monk_LVL/4</floor>+1");
		
		AB.type = "AB";
		
		rolls.Add(AB);
		
		if(fullAttack)
		{
			List<List<RollDefinition>> rollSeries = new List<List<RollDefinition>>();
			rollSeries.Add(rolls);
			
			for(int i = 1; i < AttackAction.Count; i++)
			{
				int val = (int)MathParser.Evaluate(AttackAction[i]);
				
				if(flurryOfBlows)
					val += (int)MathParser.Evaluate("<floor>Monk_LVL/4</floor> + 1");
				
				if(val > 0)
				{
					rolls = new List<RollDefinition>();
					rolls.AddRange(MagicAttackBonus);
					rolls.Add(new RollDefinition());
					
					AB = new RollDefinition();
					AB.dieCount = 0;
					AB.bonusAmount = (int)MathParser.Evaluate(val + "+" + AttackBonusFunction);
					AB.type = "AB";
					
					rolls.Add(AB);
					rollSeries.Add(rolls);
				}
				else
				{
					break;
				}
			}
			
			if(flurryOfBlows)
			{
				int flurryCount = Mathf.FloorToInt(MathParser.Evaluate("Monk_LVL") / 7.5f) + 1;

				for(int i = 0; i < flurryCount; i++)
				{
					rolls = new List<RollDefinition>();
					rolls.AddRange(MagicAttackBonus);
					rolls.Add(new RollDefinition());
					
					AB = new RollDefinition();
					AB.dieCount = 0;
					AB.bonusAmount = (int)MathParser.Evaluate(AttackAction[0] + "+" + AttackBonusFunction) - 5*i;
					AB.type = "AB";
					
					rolls.Add(AB);
					rollSeries.Add(rolls);
				}
				
				foreach(var lr in rollSeries)
				{
					lr.Find(rd => rd.type == "AB").bonusAmount -= 2;
				}
			}
			
			GameReference.reference.StartCoroutine(HelperFunctions.RollWindow(rollSeries, "Attack", this));
		}
		else
			GameReference.reference.StartCoroutine(HelperFunctions.RollWindow(rolls, "Attack"));
	}
	
	public WeaponItem()
	{
		slot = "Weapon";
	}
	
	public override void Load(string json, string file)
	{
		fileName = file;
		if(json != null)
		{
			Dictionary<string,string> data = (MiniJSON.Json.Deserialize(json) as Dictionary<string,object>).ToDSS();
			
			name = data["name"];
			description = data["description"];
			weight = float.Parse(data["weight"]);
			
			dieCount = int.Parse(data["dieCount"]);
			dieType = int.Parse(data["dieType"]);
			
			TwoHanded = bool.Parse(data["TwoHanded"]);
			Ranged = bool.Parse(data["Ranged"]);
			LightWeapon = bool.Parse(data["LightWeapon"]);
			InOffHand = bool.Parse(data["InOffHand"]);
			
			DontShowInInventory = bool.Parse(data["DontShowInInventory"]);
			
			//MagicBonus = int.Parse(data["MagicBonus"]);
			
			AdvancedAttackOverride = bool.Parse(data["AdvancedAttackOverride"]);
			AdvancedDamageOverride = bool.Parse(data["AdvancedDamageOverride"]);
			_AttackBonusFunction = data["_AttackBonusFunction"];
			_DamageBonusFunction = data["_DamageBonusFunction"];
			
			critBounds = int.Parse(data["critBounds"]);
			critMultiplier = int.Parse(data["critMultiplier"]);
			
			string attackListJSON = data["AttackAction"];
			List<string> atac = (MiniJSON.Json.Deserialize(attackListJSON) as List<object>).ConvertAll<string>(o => o as string);
			if(atac != null)
				AttackAction = atac;
			
			currentlyDrawn = bool.Parse(data["currentlyDrawn"]);
			
			buffs = new List<BuffDebuff>();
			List<object> objectsJson = MiniJSON.Json.Deserialize(data["buffs"]) as List<object>;
			foreach(string objJson in objectsJson)
			{
				BuffDebuff o = new BuffDebuff();
				o.Load(objJson, "");
				buffs.Add(o);
			}
			
			quantity = int.Parse(data["quantity"]);
			chargesRemaining = int.Parse(data["chargesRemaining"]);
			HasCharges = bool.Parse(data["HasCharges"]);
			worth = float.Parse(data["worth"]);
			
			equipped = bool.Parse(data["equipped"]);
			RequiresAmmunition = bool.Parse(data["RequiresAmmunition"]);
			
			DamageType = data["DamageType"];
		}
	}
	
	public override string ToJSON()
	{
		Dictionary<string,string> data = new Dictionary<string, string>();
		data.Add("dieCount", dieCount.ToString());
		data.Add("dieType", dieType.ToString());
		data.Add("TwoHanded", TwoHanded.ToString());
		data.Add("InOffHand", InOffHand.ToString());
		data.Add("LightWeapon", LightWeapon.ToString());
		data.Add("Ranged", Ranged.ToString());
		//data.Add("MagicBonus", MagicBonus.ToString());
		data.Add("name", name.ToString());
		data.Add("description", description.ToString());
		data.Add("weight", weight.ToString());
		data.Add("AdvancedAttackOverride", AdvancedAttackOverride.ToString());
		data.Add("AdvancedDamageOverride", AdvancedDamageOverride.ToString());
		data.Add("_AttackBonusFunction", _AttackBonusFunction);
		data.Add("_DamageBonusFunction", _DamageBonusFunction);
		data.Add("critBounds", critBounds.ToString());
		data.Add("critMultiplier", critMultiplier.ToString());
		data.Add("DontShowInInventory", DontShowInInventory.ToString());
		data.Add("currentlyDrawn", currentlyDrawn.ToString());
		data.Add("equipped", equipped.ToString());
		data.Add("RequiresAmmunition", RequiresAmmunition.ToString());
		
		data.Add("DamageType", DamageType);
		
		data.Add("AttackAction", MiniJSON.Json.Serialize(AttackAction));
		
		data.Add("buffs", buffs.ToJSON());
		data.Add("slot",slot);
		data.Add("HasCharges", HasCharges.ToString());
		data.Add("chargesRemaining", chargesRemaining.ToString());
		data.Add("quantity", quantity.ToString());
		
		data.Add("worth", worth.ToString());
		
		return MiniJSON.Json.Serialize(data);
	}
	
	public new WeaponItem DeepCopy()
	{
		WeaponItem a = new WeaponItem();
		a.Load(ToJSON(), fileName);
		return a;
	}
}
